<!DOCTYPE html> <html class><!--
 Page saved with SingleFile 
 url: https://mp.weixin.qq.com/s/5Ar5B9Ah2BdO8i9YjMQ7Qg 
 saved date: Fri Mar 27 2020 10:02:21 GMT+0800 (China Standard Time)
--><meta charset=utf-8>
<meta http-equiv=X-UA-Compatible content="IE=edge">
<meta name=viewport content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover">
<meta name=apple-mobile-web-app-capable content=yes>
<meta name=apple-mobile-web-app-status-bar-style content=black>
<meta name=color-scheme content="light dark">
<meta name=format-detection content="telephone=no">
<meta name=description content=聊聊实际项目代码是如何分层和规划的？>
<meta name=author content=hansonwong99>
<meta property=og:title content=看完这篇，别人的开源项目结构应该能看懂了>
<meta property=og:url content="http://mp.weixin.qq.com/s?__biz=MzU4ODI1MjA3NQ==&amp;mid=2247485409&amp;idx=1&amp;sn=453877c11ead49e90937885b5ac7d2f2&amp;chksm=fdded125caa958333490c0a1f7b2ca76bdc84334a8cc87adc230bb19230eac674b4f6eb0d9aa#rd">
<meta property=og:image content="http://mmbiz.qpic.cn/mmbiz_jpg/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TicQgxz4HH1gtccx4NrpicupCtUjPVND998bDmluAN6QBF2hOACa9Dh6A/0?wx_fmt=jpeg">
<meta property=og:description content=聊聊实际项目代码是如何分层和规划的？>
<meta property=og:site_name content=微信公众平台>
<meta property=og:type content=article>
<meta property=og:article:author content=hansonwong99>
<meta property=twitter:card content=summary>
<meta property=twitter:image content="http://mmbiz.qpic.cn/mmbiz_jpg/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TicQgxz4HH1gtccx4NrpicupCtUjPVND998bDmluAN6QBF2hOACa9Dh6A/0?wx_fmt=jpeg">
<meta property=twitter:title content=看完这篇，别人的开源项目结构应该能看懂了>
<meta property=twitter:creator content=hansonwong99>
<meta property=twitter:site content=微信公众平台>
<meta property=twitter:description content=聊聊实际项目代码是如何分层和规划的？>
<title>看完这篇，别人的开源项目结构应该能看懂了</title>
<style>.rich_media_inner{word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.rich_media_area_primary{padding:calc(20px + env(safe-area-inset-top)) calc(16px + env(safe-area-inset-right)) 12px calc(16px + env(safe-area-inset-left))}.rich_media_area_extra{padding:0 calc(16px + env(safe-area-inset-right)) calc(16px + env(safe-area-inset-bottom)) calc(16px + env(safe-area-inset-left))}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.6}body{-webkit-touch-callout:none;color:#333;background-color:#f2f2f2;letter-spacing:.034em}h2{font-weight:400}a{-webkit-tap-highlight-color:rgba(0,0,0,0)}.rich_media_title{font-size:22px;line-height:1.4;margin-bottom:14px}@supports (-webkit-overflow-scrolling:touch){.rich_media_title{font-weight:700}}.rich_media_meta_list{margin-bottom:22px;line-height:20px;font-size:0;word-wrap:break-word;word-break:break-all}.rich_media_meta_list em{font-style:normal}.rich_media_meta{display:inline-block;vertical-align:middle;margin:0 10px 10px 0;font-size:15px;-webkit-tap-highlight-color:rgba(0,0,0,0)}.rich_media_meta.appmsg_title_tag{margin-right:8px}.rich_media_meta_text{color:rgba(0,0,0,0.3)}.rich_media_meta_nickname{position:relative}.rich_media_content{overflow:hidden;color:#333;font-size:17px;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto;text-align:justify;z-index:0}.rich_media_content *{max-width:100%!important;box-sizing:border-box!important;-webkit-box-sizing:border-box!important;word-wrap:break-word!important}.rich_media_content p{clear:both;min-height:1em}.rich_media_content img{height:auto!important}blockquote{padding-left:10px;border-left:3px solid #dbdbdb;padding-top:4px}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DISABLED-FONT-COLOR:rgba(255,255,255,0.2)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DEFAULT-BG:rgba(255,255,255,0.08)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DEFAULT-COLOR:rgba(255,255,255,0.8)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DEFAULT-ACTIVE-BG:rgba(122,122,122,0.1536)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-DIALOG-LINE-COLOR:rgba(255,255,255,0.1)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BG-0:#191919;--weui-BG-1:#1f1f1f;--weui-BG-2:#232323;--weui-BG-3:#2f2f2f;--weui-BG-4:#606060;--weui-BG-5:#2c2c2c;--weui-FG-0:rgba(255,255,255,0.8);--weui-FG-HALF:rgba(255,255,255,0.6);--weui-FG-1:rgba(255,255,255,0.5);--weui-FG-2:rgba(255,255,255,0.3);--weui-FG-3:rgba(255,255,255,0.05);--weui-RED:#fa5151;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#28b561;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-WHITE:rgba(255,255,255,0.8);--weui-LINK:#7d90a9;--weui-TEXTGREEN:#259c5c;--weui-FG:white;--weui-BG:black;--weui-TAG-TEXT-ORANGE:rgba(250,157,59,0.6);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,0.1);--weui-TAG-TEXT-GREEN:rgba(6,174,86,0.6);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,0.1);--weui-TAG-TEXT-BLUE:rgba(16,174,255,0.6);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,0.1);--weui-TAG-TEXT-BLACK:rgba(255,255,255,0.5);--weui-TAG-BACKGROUND-BLACK:rgba(255,255,255,0.05)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BG-COLOR-ACTIVE:#282828}}.weui-flex{display:-webkit-box;display:-webkit-flex}.weui-flex__item{-webkit-box-flex:1;-webkit-flex:1}.icon_appmsg_tag{display:inline-block;vertical-align:middle;font-size:12px;line-height:1.67;background-color:#c3c3c3;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px;width:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;max-width:70%}.icon_appmsg_tag.appmsg_title_tag{background:#f2f2f2;color:rgba(0,0,0,0.3);padding:0 4px}@-webkit-keyframes loading{0%{-webkit-transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg)}}@keyframes loading{0%{-webkit-transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg)}}.appmsg_skin_default .rich_media_area_primary{background-color:#fff}.appmsg_style_default .rich_media_tool{padding-top:15px}.read-more__area{margin:30px 0}.rich_media_wrp{position:relative}.meta_praise{-webkit-tap-highlight-color:rgba(0,0,0,0)}.meta_praise:hover{cursor:pointer}.like_btn{-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.like_btn:before{content:"";display:inline-block;vertical-align:middle;font-size:24px;width:1em;height:1em;margin-top:-0.15em;margin-right:2px;background:transparent url("data:image/svg+xml;charset=utf8, %3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.145 5.254l2.623-2.653a1.726 1.726 0 0 1 2.464 0l2.623 2.653 3.585.973c.93.25 1.485 1.222 1.236 2.17l-.96 3.622.96 3.625c.25.946-.306 1.918-1.234 2.168l-3.56.965-2.65 2.671c-.682.69-1.782.69-2.464 0l-2.623-2.654-3.585-.972c-.93-.25-1.485-1.222-1.236-2.17l.96-3.623-.96-3.634c-.25-.946.306-1.918 1.234-2.168l3.587-.973zm.874 1.55l.007 2.461a1.8 1.8 0 0 1-.867 1.54l-2.11 1.224 2.1 1.218c.541.31.877.9.877 1.536l-.007 2.462 2.106-1.238a1.713 1.713 0 0 1 1.754-.004l2.102 1.236-.007-2.466c0-.624.333-1.21.867-1.53l2.11-1.224-2.1-1.217c-.541-.31-.877-.9-.877-1.537l.007-2.461-2.106 1.238a1.695 1.695 0 0 1-1.748.002L9.02 6.804zM7.874 9.267l-.007-2.719-3.015.818a.593.593 0 0 0-.417.723l.808 3.053 2.335-1.354a.616.616 0 0 0 .296-.521zm-.291 5.006l-2.34-1.357-.807 3.043a.593.593 0 0 0 .418.724l3.013.817.007-2.717a.59.59 0 0 0-.291-.51zm4.12 2.752l-2.32 1.365 2.195 2.22c.233.236.611.236.844 0l2.203-2.22-2.318-1.365a.585.585 0 0 0-.603 0zm4.423-2.253l.007 2.718 3.015-.817c.313-.085.499-.41.417-.722l-.808-3.045-2.335 1.354a.612.612 0 0 0-.296.512zm.291-4.996l2.34 1.357.807-3.043a.593.593 0 0 0-.418-.724l-3.013-.818-.007 2.718c0 .21.112.407.291.51zm-4.713-2.752a.57.57 0 0 0 .592 0l2.32-1.366-2.194-2.22a.592.592 0 0 0-.844 0l-2.195 2.22 2.32 1.366z' fill='%23576B95' fill-rule='evenodd'/%3E%3C/svg%3E") 0 0 no-repeat;background-size:1em}.like_comment_wrp:before,.like_comment_wrp:after{content:"";display:inline-block;width:0;height:0;border-width:7px;border-style:dashed;border-color:transparent;border-top-width:0;border-bottom-color:rgba(0,0,0,0.03);border-bottom-style:solid;position:absolute;top:-7px;right:28px}.like_comment_wrp:after{border-bottom-color:#f7f7f7;top:-6px}.like_comment_inner{display:-webkit-box;display:-webkit-flex;-webkit-box-align:center;-webkit-align-items:center}.like_comment_primary_wrp:before,.like_comment_primary_wrp:after{content:"";display:inline-block;width:0;height:0;border-width:7px;border-style:dashed;border-color:transparent;border-top-width:0;border-bottom-color:#fff;border-bottom-style:solid;position:absolute;top:-7px;right:28px}.like_comment_primary_wrp:after{border-bottom-color:#fff;top:-6px}.like_comment_primary_wrp.editing:before,.like_comment_primary_wrp.editing:after{display:none}@media screen and (min-width:1024px){.rich_media_area_primary_inner,.rich_media_area_extra_inner{max-width:677px;margin-left:auto;margin-right:auto}.rich_media_area_primary{padding-top:32px}}@media (prefers-color-scheme:dark){.icon_appmsg_tag{background-color:rgba(255,255,255,0.15)}.icon_appmsg_tag.appmsg_title_tag{background:rgba(255,255,255,0.05);color:rgba(255,255,255,0.5)}body{color:rgba(255,255,255,0.8)}.tips_global_primary.tips_global_primary{color:rgba(255,255,255,0.3)}.appmsg_skin_default .rich_media_area_primary{background-color:#232323}body{background-color:#191919}body a{color:#7d90a9}.rich_media_content{color:rgba(255,255,255,0.8)}.rich_media_content img{-webkit-filter:brightness(0.7);filter:brightness(0.7)}.rich_media_meta_text{color:rgba(255,255,255,0.3)}blockquote{border-color:#606060}}</style>
<!--[if lt IE 9]>
<link rel="stylesheet" type="text/css" href="//res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg_new/pc4c4161.css">
<![endif]-->
<style id=page/appmsg_new/not_in_mm.css>@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-BTN-DISABLED-FONT-COLOR:hsla(0,0%,100%,.2)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-BTN-DEFAULT-BG:hsla(0,0%,100%,.08)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-BTN-DEFAULT-COLOR:hsla(0,0%,100%,.8)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-BTN-DEFAULT-ACTIVE-BG:hsla(0,0%,48%,.1536)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-DIALOG-LINE-COLOR:hsla(0,0%,100%,.1)}}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}*{margin:0;padding:0}a{-webkit-tap-highlight-color:rgba(0,0,0,0)}::-webkit-input-placeholder{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-BG-0:#191919;--weui-BG-1:#1f1f1f;--weui-BG-2:#232323;--weui-BG-3:#2f2f2f;--weui-BG-4:#606060;--weui-BG-5:#2c2c2c;--weui-FG-0:hsla(0,0%,100%,.8);--weui-FG-HALF:hsla(0,0%,100%,.6);--weui-FG-1:hsla(0,0%,100%,.5);--weui-FG-2:hsla(0,0%,100%,.3);--weui-FG-3:hsla(0,0%,100%,.05);--weui-RED:#fa5151;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#28b561;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-WHITE:hsla(0,0%,100%,.8);--weui-LINK:#7d90a9;--weui-TEXTGREEN:#259c5c;--weui-FG:#fff;--weui-BG:#000;--weui-TAG-TEXT-ORANGE:rgba(250,157,59,.6);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,.1);--weui-TAG-TEXT-GREEN:rgba(6,174,86,.6);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-BLUE:rgba(16,174,255,.6);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,.1);--weui-TAG-TEXT-BLACK:hsla(0,0%,100%,.5);--weui-TAG-BACKGROUND-BLACK:hsla(0,0%,100%,.05)}}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]){--weui-BG-COLOR-ACTIVE:#282828}}.weui-flex{display:-webkit-box;display:-webkit-flex}.weui-flex__item{-webkit-box-flex:1;-webkit-flex:1}.weui-dialog{-webkit-transform:translateY(-50%);display:-webkit-box;display:-webkit-flex;-webkit-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal}@media screen and (min-width:352px){.weui-dialog{width:320px;margin:0 auto}}.weui-toast{-webkit-transform:translate(-50%,-50%);display:-webkit-box;display:-webkit-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-webkit-box-align:center;-webkit-align-items:center;-webkit-box-pack:center;-webkit-justify-content:center}@media (prefers-color-scheme:dark){body:not([data-weui-theme=light]) .weui-toast{background-color:#606060}}@-webkit-keyframes a{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes a{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes b{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes b{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@-webkit-keyframes c{0%{opacity:0}to{opacity:1}}@keyframes c{0%{opacity:0}to{opacity:1}}@-webkit-keyframes d{0%{opacity:1}to{opacity:0}}@keyframes d{0%{opacity:1}to{opacity:0}}@-webkit-keyframes e{0%{-webkit-transform:translateX(0);transform:translateX(0)}16%{-webkit-transform:translateX(-8px);transform:translateX(-8px)}28%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}44%{-webkit-transform:translateX(0);transform:translateX(0)}59%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}73%{-webkit-transform:translateX(0);transform:translateX(0)}82%{-webkit-transform:translateX(16px);transform:translateX(16px)}94%{-webkit-transform:translateX(8px);transform:translateX(8px)}to{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes e{0%{-webkit-transform:translateX(0);transform:translateX(0)}16%{-webkit-transform:translateX(-8px);transform:translateX(-8px)}28%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}44%{-webkit-transform:translateX(0);transform:translateX(0)}59%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}73%{-webkit-transform:translateX(0);transform:translateX(0)}82%{-webkit-transform:translateX(16px);transform:translateX(16px)}94%{-webkit-transform:translateX(8px);transform:translateX(8px)}to{-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes f{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes f{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}body{--weui-BTN-DISABLED-FONT-COLOR:rgba(0,0,0,0.2)}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DISABLED-FONT-COLOR:rgba(255,255,255,0.2)}}body{--weui-BTN-DEFAULT-BG:#f2f2f2}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DEFAULT-BG:rgba(255,255,255,0.08)}}body{--weui-BTN-DEFAULT-COLOR:#06ae56}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DEFAULT-COLOR:rgba(255,255,255,0.8)}}body{--weui-BTN-DEFAULT-ACTIVE-BG:#e6e6e6}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BTN-DEFAULT-ACTIVE-BG:rgba(122,122,122,0.1536)}}body{--weui-DIALOG-LINE-COLOR:rgba(0,0,0,0.1)}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-DIALOG-LINE-COLOR:rgba(255,255,255,0.1)}}body{--weui-BG-0:#ededed;--weui-BG-1:#f7f7f7;--weui-BG-2:#fff;--weui-BG-3:#f7f7f7;--weui-BG-4:#4c4c4c;--weui-BG-5:#fff;--weui-FG-0:rgba(0,0,0,0.9);--weui-FG-HALF:rgba(0,0,0,0.9);--weui-FG-1:rgba(0,0,0,0.5);--weui-FG-2:rgba(0,0,0,0.3);--weui-FG-3:rgba(0,0,0,0.1);--weui-RED:#fa5151;--weui-ORANGE:#fa9d3b;--weui-YELLOW:#ffc300;--weui-GREEN:#91d300;--weui-LIGHTGREEN:#95ec69;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1485ee;--weui-PURPLE:#6467f0;--weui-WHITE:#fff;--weui-LINK:#576b95;--weui-TEXTGREEN:#06ae56;--weui-FG:black;--weui-BG:white;--weui-TAG-TEXT-ORANGE:#fa9d3b;--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,0.1);--weui-TAG-TEXT-GREEN:#06ae56;--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,0.1);--weui-TAG-TEXT-BLUE:#10aeff;--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,0.1);--weui-TAG-TEXT-BLACK:rgba(0,0,0,0.5);--weui-TAG-BACKGROUND-BLACK:rgba(0,0,0,0.05)}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BG-0:#191919;--weui-BG-1:#1f1f1f;--weui-BG-2:#232323;--weui-BG-3:#2f2f2f;--weui-BG-4:#606060;--weui-BG-5:#2c2c2c;--weui-FG-0:rgba(255,255,255,0.8);--weui-FG-HALF:rgba(255,255,255,0.6);--weui-FG-1:rgba(255,255,255,0.5);--weui-FG-2:rgba(255,255,255,0.3);--weui-FG-3:rgba(255,255,255,0.05);--weui-RED:#fa5151;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#28b561;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-WHITE:rgba(255,255,255,0.8);--weui-LINK:#7d90a9;--weui-TEXTGREEN:#259c5c;--weui-FG:white;--weui-BG:black;--weui-TAG-TEXT-ORANGE:rgba(250,157,59,0.6);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,0.1);--weui-TAG-TEXT-GREEN:rgba(6,174,86,0.6);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,0.1);--weui-TAG-TEXT-BLUE:rgba(16,174,255,0.6);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,0.1);--weui-TAG-TEXT-BLACK:rgba(255,255,255,0.5);--weui-TAG-BACKGROUND-BLACK:rgba(255,255,255,0.05)}}body{--weui-BG-COLOR-ACTIVE:#ececec}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']){--weui-BG-COLOR-ACTIVE:#282828}}.weui-flex{display:-webkit-box;display:-webkit-flex;display:flex}.weui-flex__item{-webkit-box-flex:1;-webkit-flex:1;flex:1}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{line-height:1.6;font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;font-size:16px}body,h2,p,ul{margin:0}a{color:var(--weui-LINK);text-decoration:none}body,html{-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media all and (orientation:portrait){@-webkit-keyframes opr_fade_out{0%{filter:alpha(opacity = 100);-moz-opacity:1;-khtml-opacity:1;opacity:1}100%{filter:alpha(opacity = 0);-moz-opacity:0;-khtml-opacity:0;opacity:0}}@-webkit-keyframes opr_fade_in{0%{filter:alpha(opacity = 0);-moz-opacity:0;-khtml-opacity:0;opacity:0}100%{bottom:0;filter:alpha(opacity = 100);-moz-opacity:1;-khtml-opacity:1;opacity:1}}}@-webkit-keyframes opr_fade_out{0%{filter:alpha(opacity = 100);-moz-opacity:1;-khtml-opacity:1;opacity:1}100%{filter:alpha(opacity = 0);-moz-opacity:0;-khtml-opacity:0;opacity:0}}@-webkit-keyframes opr_fade_in{0%{filter:alpha(opacity = 0);-moz-opacity:0;-khtml-opacity:0;opacity:0}100%{filter:alpha(opacity = 100);-moz-opacity:1;-khtml-opacity:1;opacity:1}}@-webkit-keyframes opacity-60-25-0-12{0%{opacity:.25}0.01%{opacity:.25}0.02%{opacity:1}60.01%{opacity:.25}100%{opacity:.25}}@-webkit-keyframes opacity-60-25-1-12{0%{opacity:.25}8.34333%{opacity:.25}8.35333%{opacity:1}68.3433%{opacity:.25}100%{opacity:.25}}@-webkit-keyframes opacity-60-25-2-12{0%{opacity:.25}16.6767%{opacity:.25}16.6867%{opacity:1}76.6767%{opacity:.25}100%{opacity:.25}}@-webkit-keyframes opacity-60-25-3-12{0%{opacity:.25}25.01%{opacity:.25}25.02%{opacity:1}85.01%{opacity:.25}100%{opacity:.25}}@-webkit-keyframes opacity-60-25-4-12{0%{opacity:.25}33.3433%{opacity:.25}33.3533%{opacity:1}93.3433%{opacity:.25}100%{opacity:.25}}@-webkit-keyframes opacity-60-25-5-12{0%{opacity:.270958333333333}41.6767%{opacity:.25}41.6867%{opacity:1}1.67667%{opacity:.25}100%{opacity:.270958333333333}}@-webkit-keyframes opacity-60-25-6-12{0%{opacity:.375125}50.01%{opacity:.25}50.02%{opacity:1}10.01%{opacity:.25}100%{opacity:.375125}}@-webkit-keyframes opacity-60-25-7-12{0%{opacity:.479291666666667}58.3433%{opacity:.25}58.3533%{opacity:1}18.3433%{opacity:.25}100%{opacity:.479291666666667}}@-webkit-keyframes opacity-60-25-8-12{0%{opacity:.583458333333333}66.6767%{opacity:.25}66.6867%{opacity:1}26.6767%{opacity:.25}100%{opacity:.583458333333333}}@-webkit-keyframes opacity-60-25-9-12{0%{opacity:.687625}75.01%{opacity:.25}75.02%{opacity:1}35.01%{opacity:.25}100%{opacity:.687625}}@-webkit-keyframes opacity-60-25-10-12{0%{opacity:.791791666666667}83.3433%{opacity:.25}83.3533%{opacity:1}43.3433%{opacity:.25}100%{opacity:.791791666666667}}@-webkit-keyframes opacity-60-25-11-12{0%{opacity:.895958333333333}91.6767%{opacity:.25}91.6867%{opacity:1}51.6767%{opacity:.25}100%{opacity:.895958333333333}}@-webkit-keyframes loading{0%{-webkit-transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg)}}@keyframes loading{0%{-webkit-transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg)}}.tc{text-align:center}.tips_global{color:#888}.tips_global_primary{color:rgba(0,0,0,0.3)}.reward_area{box-sizing:border-box;margin:0 auto;padding:35px 5% 20px;line-height:1.4;color:rgba(0,0,0,0.9)}.reward_area_inner{margin:0 auto;position:relative;left:3px}.reward-avatar{width:48px;height:48px;margin:0 auto;overflow:hidden}.reward-author{text-align:center}.reward_tips{font-size:17px;min-height:1em;text-align:center}.reward_tips:before,.reward_tips:after{font-family:"PingFang SC","Helvetica Neue",sans-serif}.reward_tips:before{content:'“';margin-right:.34em}.reward_tips:after{content:'”';margin-left:.34em}.reward_qrcode_area{margin:38px 0 20px;padding:30px 20px;font-size:14px;border:1px solid #ebebeb}.reward_qrcode_area p{word-wrap:break-word;word-break:break-all}.reward_qrcode_area .tips_global{font-size:13px}.reward_qrcode_area .reward_tips{margin-top:1em;margin-bottom:0}.reward_qrcode_img_wrp{width:200px;height:200px;background-color:#fff;display:block;margin:1.5em auto 1.6em}@media (min-device-width:414px){.reward_qrcode_area .tips_global{line-height:1.8}.reward_qrcode_img_wrp{width:224px;height:224px;margin:1.8em auto}}.reward_area_primary .reward-avatar{border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px}.reward_area_primary .reward-author{margin-top:10px;font-size:15px}@supports (-webkit-overflow-scrolling:touch){.reward_button{font-weight:700}}.reward_area_carry_whisper .reward_button_wrp{margin-bottom:16px}.reward_area_carry_whisper .reward_area_inner{padding-top:16px}.reward_whisper_wrp{background-color:rgba(0,0,0,0.03);border-radius:4px;overflow:hidden;padding:16px 16px;display:-webkit-box;display:-webkit-flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;text-align:center;line-height:1.4;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;font-size:15px}.reward_button_wrp{position:relative}.rich_media_extra{position:relative}.top_banner{background-color:#fff}.like_btn{-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0);outline:0;background-color:transparent;border:0;display:inline-block;vertical-align:middle;white-space:nowrap;padding:0;font-size:15px;font-family:inherit;line-height:2.13333333;color:#576b95}.like_btn:before{content:"";display:inline-block;vertical-align:middle;font-size:24px;width:1em;height:1em;margin-top:-0.15em;margin-right:2px;background:transparent url("data:image/svg+xml;charset=utf8, %3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.145 5.254l2.623-2.653a1.726 1.726 0 0 1 2.464 0l2.623 2.653 3.585.973c.93.25 1.485 1.222 1.236 2.17l-.96 3.622.96 3.625c.25.946-.306 1.918-1.234 2.168l-3.56.965-2.65 2.671c-.682.69-1.782.69-2.464 0l-2.623-2.654-3.585-.972c-.93-.25-1.485-1.222-1.236-2.17l.96-3.623-.96-3.634c-.25-.946.306-1.918 1.234-2.168l3.587-.973zm.874 1.55l.007 2.461a1.8 1.8 0 0 1-.867 1.54l-2.11 1.224 2.1 1.218c.541.31.877.9.877 1.536l-.007 2.462 2.106-1.238a1.713 1.713 0 0 1 1.754-.004l2.102 1.236-.007-2.466c0-.624.333-1.21.867-1.53l2.11-1.224-2.1-1.217c-.541-.31-.877-.9-.877-1.537l.007-2.461-2.106 1.238a1.695 1.695 0 0 1-1.748.002L9.02 6.804zM7.874 9.267l-.007-2.719-3.015.818a.593.593 0 0 0-.417.723l.808 3.053 2.335-1.354a.616.616 0 0 0 .296-.521zm-.291 5.006l-2.34-1.357-.807 3.043a.593.593 0 0 0 .418.724l3.013.817.007-2.717a.59.59 0 0 0-.291-.51zm4.12 2.752l-2.32 1.365 2.195 2.22c.233.236.611.236.844 0l2.203-2.22-2.318-1.365a.585.585 0 0 0-.603 0zm4.423-2.253l.007 2.718 3.015-.817c.313-.085.499-.41.417-.722l-.808-3.045-2.335 1.354a.612.612 0 0 0-.296.512zm.291-4.996l2.34 1.357.807-3.043a.593.593 0 0 0-.418-.724l-3.013-.818-.007 2.718c0 .21.112.407.291.51zm-4.713-2.752a.57.57 0 0 0 .592 0l2.32-1.366-2.194-2.22a.592.592 0 0 0-.844 0l-2.195 2.22 2.32 1.366z' fill='%23576B95' fill-rule='evenodd'/%3E%3C/svg%3E") 0 0 no-repeat;background-size:1em}.like_num{font-size:15px;margin-left:.2em}.like_comment_wrp{font-size:17px;margin-top:9px;margin-bottom:8px;position:relative;z-index:1}.like_comment_wrp:before,.like_comment_wrp:after{content:"";display:inline-block;width:0;height:0;border-width:7px;border-style:dashed;border-color:transparent;border-top-width:0;border-bottom-color:rgba(0,0,0,0.03);border-bottom-style:solid;position:absolute;top:-7px;right:28px}.like_comment_wrp:after{border-bottom-color:#f7f7f7;top:-6px}.like_comment_inner{background-color:rgba(0,0,0,0.03);border-radius:4px;overflow:hidden;padding:24px 16px;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;text-align:center}.like_comment_primary_wrp{font-size:16px;margin-top:9px;margin-bottom:4px;background-color:#fff;z-index:21}.like_comment_primary_wrp:before,.like_comment_primary_wrp:after{content:"";display:inline-block;width:0;height:0;border-width:7px;border-style:dashed;border-color:transparent;border-top-width:0;border-bottom-color:#fff;border-bottom-style:solid;position:absolute;top:-7px;right:28px}.like_comment_primary_wrp:after{border-bottom-color:#fff;top:-6px}.like_comment_primary_wrp.editing{position:fixed;top:16px;bottom:0;left:0;right:0;border-top-left-radius:12px;border-top-right-radius:12px;margin:0}.like_comment_primary_wrp.editing:before,.like_comment_primary_wrp.editing:after{display:none}.like_comment_primary_mask{position:fixed;z-index:20;top:0;left:0;bottom:0;right:0;background-color:rgba(0,0,0,0.2)}@-webkit-keyframes weuiLoading{0%{-webkit-transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg)}}@keyframes weuiLoading{0%{-webkit-transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg)}}.mpda_bottom_container{position:relative}.rich_media_tool{overflow:hidden;line-height:32px}.rich_media_tool .meta_primary{float:left}.rich_media_tool .meta_extra{float:right}.rich_media_tool .meta_praise{text-align:right}.media_tool_meta i{vertical-align:0;position:relative;top:1px}.meta_praise{-webkit-tap-highlight-color:rgba(0,0,0,0);outline:0}.meta_praise .praise_num{display:inline-block;vertical-align:top}.meta_praise:hover{cursor:pointer}.icon_praise_gray{background:transparent url() no-repeat 0 0;display:inline-block}.rich_media_tool{font-size:15px}.rich_media_tool .meta_primary{margin-right:16px}.rich_media_tool .meta_extra{margin-left:16px;color:#576b95}.rich_media_tool .meta_praise{min-width:2.5em}.rich_media_tool .meta_praise i{margin-right:5px}.icon_praise_gray{background-image:url("data:image/svg+xml;charset=utf8, %3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 6.988h-.003c-.095-.01-.167-.022-.125-.022H1.75c-.343 0-.75.39-.75.7v6.73c0 .31.27.57.611.57H2.5V7.01a.51.51 0 0 1 0-.022zm1 .003a.55.55 0 0 1 0 .02v7.955h7.414c.748 0 1.395-.361 1.773-1.324a37.17 37.17 0 0 0 1.115-2.57c.219-.564.413-1.11.575-1.627.247-.785.413-1.48.484-2.058.073-.595-.565-1.021-1.236-1.021h-4.97l.102-.586.18-1.027.13-.55a35.058 35.058 0 0 0 .245-1.128c.212-1.098-.483-2.019-1.238-2.067-.74-.048-1.1.111-1.104.562-.008 1.276-.45 2.805-1.252 4.129-.357.589-.899.965-1.56 1.16-.217.065-.438.107-.658.132zm6.345-1.625h3.78c1.19 0 2.393.804 2.229 2.143-.08.646-.26 1.397-.523 2.235-.17.54-.37 1.107-.597 1.69a38.158 38.158 0 0 1-1.133 2.61c-.525 1.346-1.557 1.922-2.687 1.922H1.61c-.886 0-1.611-.698-1.611-1.57v-6.73c0-.871.864-1.7 1.75-1.7l.719.009A3.285 3.285 0 0 0 3.876 5.9c.435-.13.769-.361.986-.72.71-1.171 1.102-2.525 1.108-3.618C5.978.338 6.901-.07 8.14.01c1.36.088 2.48 1.57 2.155 3.255a36.012 36.012 0 0 1-.253 1.167l-.124.52-.072.414z' fill='%23576B95' fill-rule='nonzero'/%3E%3C/svg%3E");font-size:16px;width:1em;height:1em;background-size:1em}.praise_num{color:#576b95}a,button{cursor:pointer}.rich_media_extra{overflow:hidden}.rich_media_extra_discuss{padding-top:0}.praise_num:empty{margin-left:-3px}.comment_primary_emotion_panel_wrp{position:absolute;z-index:1;padding-top:8px;padding-bottom:16px}.comment_primary_emotion_panel{background:#fff;box-shadow:0 2px 8px 0 rgba(0,0,0,0.16);border-radius:4px;width:376px;height:216px;overflow-y:auto;-webkit-overflow-scrolling:touch}@-webkit-keyframes slidein{from{-webkit-transform:translateX(-50%);transform:translateX(-50%)}to{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slidein{from{-webkit-transform:translateX(-50%);transform:translateX(-50%)}to{-webkit-transform:translateX(0);transform:translateX(0)}}.weui-dialog{position:fixed;z-index:5000;top:50%;left:16px;right:16px;-webkit-transform:translate(0,-50%);transform:translate(0,-50%);background-color:var(--weui-BG-2);text-align:center;border-radius:12px;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;max-height:90%}@media screen and (min-width:352px){.weui-dialog{width:320px;margin:0 auto}}.weui-toast{position:fixed;z-index:5000;width:120px;height:120px;top:40%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;border-radius:5px;color:rgba(255,255,255,0.9);display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;background-color:#4c4c4c}@media (prefers-color-scheme:dark){body:not([data-weui-theme='light']) .weui-toast{background-color:#606060}}.weui-mask{position:fixed;z-index:1000;top:0;right:0;left:0;bottom:0;background:rgba(0,0,0,0.6)}.weui-mask_transparent{position:fixed;z-index:1000;top:0;right:0;left:0;bottom:0}@-webkit-keyframes weuiLoading{0%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg);transform:rotate3d(0,0,1,360deg)}}@keyframes weuiLoading{0%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}100%{-webkit-transform:rotate3d(0,0,1,360deg);transform:rotate3d(0,0,1,360deg)}}@-webkit-keyframes weuiAudioPlaying{30%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M7.97 15a4.251 4.251 0 0 0 1.23-3 4.25 4.25 0 0 0-1.23-3L5 12l2.97 3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M7.97 15a4.251 4.251 0 0 0 1.23-3 4.25 4.25 0 0 0-1.23-3L5 12l2.97 3z'/%3E%3C/svg%3E")}31%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}61%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}62%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}100%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}}@keyframes weuiAudioPlaying{30%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M7.97 15a4.251 4.251 0 0 0 1.23-3 4.25 4.25 0 0 0-1.23-3L5 12l2.97 3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M7.97 15a4.251 4.251 0 0 0 1.23-3 4.25 4.25 0 0 0-1.23-3L5 12l2.97 3z'/%3E%3C/svg%3E")}31%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}61%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M11.435 5.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}62%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}100%{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E  %3Cpath fill='%2307C160' d='M14.9 2A14.17 14.17 0 0 1 19 12a14.17 14.17 0 0 1-4.1 10l-1.485-1.5A12.036 12.036 0 0 0 16.9 12c0-3.233-1.267-6.259-3.485-8.5L14.899 2zm-3.465 3.5A9.21 9.21 0 0 1 14.1 12a9.21 9.21 0 0 1-2.665 6.5L9.95 17A7.077 7.077 0 0 0 12 12a7.077 7.077 0 0 0-2.05-5l1.485-1.5zM7.97 9a4.251 4.251 0 0 1 1.23 3 4.25 4.25 0 0 1-1.23 3L5 12l2.97-3z'/%3E%3C/svg%3E")}}.weui-desktop-popover{white-space:normal;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto;z-index:500;color:#353535;line-height:1.6;background:#fff;border-radius:2px}.weui-desktop-popover:before{content:" ";width:8px;height:8px;background-color:#fff;box-shadow:0 2px 10px 0 #d4d4d4;transform:matrix(0.71,0.71,-0.71,0.71,0,0);-ms-transform:matrix(0.71,0.71,-0.71,0.71,0,0);-webkit-transform:matrix(0.71,0.71,-0.71,0.71,0,0);position:absolute}.weui-desktop-popover:after{content:" ";background-color:#fff;position:absolute}.weui-desktop-popover_img-text{text-align:center}.weui-desktop-popover_pos-up-center{margin-top:16px}.weui-desktop-popover_pos-up-left:before,.weui-desktop-popover_pos-up-center:before,.weui-desktop-popover_pos-up-right:before{top:-4px}.weui-desktop-popover_pos-up-left:after,.weui-desktop-popover_pos-up-center:after,.weui-desktop-popover_pos-up-right:after{height:10px;top:0;left:0;right:0}.weui-desktop-popover_pos-up-center:before,.weui-desktop-popover_pos-down-center:before{margin-left:-4px}.weui-desktop-popover{position:absolute;padding:14px;box-shadow:none;border:1px solid #d9dadc;width:182px;box-sizing:border-box}.weui-desktop-popover:before{box-shadow:none;border:1px solid #d9dadc}.function_hd{padding:14px 16px 8px;font-size:15px;color:rgba(0,0,0,0.5);font-weight:400;line-height:1.35}.function_mod{background:#fff;border-radius:8px;margin:16px 0}@media screen and (min-width:1024px){.function_hd{padding-left:24px;padding-right:24px}}@media screen and (max-width:1023px){.profile_container{display:none!important}}@media (prefers-color-scheme:dark){.function_mod{background:#191919}}@media (prefers-color-scheme:dark){body .weui-dialog{background:#2c2c2c}.praise_num{color:#7d90a9}.like_comment_primary_wrp.editing{background-color:#2c2c2c}.like_comment_primary_wrp.editing{box-shadow:0 -1px 10px 0 rgba(0,0,0,0.3)}.like_btn:before{background-image:url("data:image/svg+xml;charset=utf8, %3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.145 5.254l2.623-2.653a1.726 1.726 0 0 1 2.464 0l2.623 2.653 3.585.973c.93.25 1.485 1.222 1.236 2.17l-.96 3.622.96 3.625c.25.946-.306 1.918-1.234 2.168l-3.56.965-2.65 2.671c-.682.69-1.782.69-2.464 0l-2.623-2.654-3.585-.972c-.93-.25-1.485-1.222-1.236-2.17l.96-3.623-.96-3.634c-.25-.946.306-1.918 1.234-2.168l3.587-.973zm.874 1.55l.007 2.461a1.8 1.8 0 0 1-.867 1.54l-2.11 1.224 2.1 1.218c.541.31.877.9.877 1.536l-.007 2.462 2.106-1.238a1.713 1.713 0 0 1 1.754-.004l2.102 1.236-.007-2.466c0-.624.333-1.21.867-1.53l2.11-1.224-2.1-1.217c-.541-.31-.877-.9-.877-1.537l.007-2.461-2.106 1.238a1.695 1.695 0 0 1-1.748.002L9.02 6.804zM7.874 9.267l-.007-2.719-3.015.818a.593.593 0 0 0-.417.723l.808 3.053 2.335-1.354a.616.616 0 0 0 .296-.521zm-.291 5.006l-2.34-1.357-.807 3.043a.593.593 0 0 0 .418.724l3.013.817.007-2.717a.59.59 0 0 0-.291-.51zm4.12 2.752l-2.32 1.365 2.195 2.22c.233.236.611.236.844 0l2.203-2.22-2.318-1.365a.585.585 0 0 0-.603 0zm4.423-2.253l.007 2.718 3.015-.817c.313-.085.499-.41.417-.722l-.808-3.045-2.335 1.354a.612.612 0 0 0-.296.512zm.291-4.996l2.34 1.357.807-3.043a.593.593 0 0 0-.418-.724l-3.013-.818-.007 2.718c0 .21.112.407.291.51zm-4.713-2.752a.57.57 0 0 0 .592 0l2.32-1.366-2.194-2.22a.592.592 0 0 0-.844 0l-2.195 2.22 2.32 1.366z' fill='%237D90A9' fill-rule='evenodd'/%3E%3C/svg%3E")}.like_btn{color:#7d90a9}.like_comment_inner{background-color:rgba(51,51,51,0.9)}.like_comment_wrp:before,.like_comment_wrp:after{border-bottom-color:rgba(51,51,51,0.9);right:16px}.icon_praise_gray{background-image:url("data:image/svg+xml;charset=utf8, %3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 6.988h-.003c-.095-.01-.167-.022-.125-.022H1.75c-.343 0-.75.39-.75.7v6.73c0 .31.27.57.611.57H2.5V7.01a.51.51 0 0 1 0-.022zm1 .003a.55.55 0 0 1 0 .02v7.955h7.414c.748 0 1.395-.361 1.773-1.324a37.17 37.17 0 0 0 1.115-2.57c.219-.564.413-1.11.575-1.627.247-.785.413-1.48.484-2.058.073-.595-.565-1.021-1.236-1.021h-4.97l.102-.586.18-1.027.13-.55a35.058 35.058 0 0 0 .245-1.128c.212-1.098-.483-2.019-1.238-2.067-.74-.048-1.1.111-1.104.562-.008 1.276-.45 2.805-1.252 4.129-.357.589-.899.965-1.56 1.16-.217.065-.438.107-.658.132zm6.345-1.625h3.78c1.19 0 2.393.804 2.229 2.143-.08.646-.26 1.397-.523 2.235-.17.54-.37 1.107-.597 1.69a38.158 38.158 0 0 1-1.133 2.61c-.525 1.346-1.557 1.922-2.687 1.922H1.61c-.886 0-1.611-.698-1.611-1.57v-6.73c0-.871.864-1.7 1.75-1.7l.719.009A3.285 3.285 0 0 0 3.876 5.9c.435-.13.769-.361.986-.72.71-1.171 1.102-2.525 1.108-3.618C5.978.338 6.901-.07 8.14.01c1.36.088 2.48 1.57 2.155 3.255a36.012 36.012 0 0 1-.253 1.167l-.124.52-.072.414z' fill='%237D90A9' fill-rule='nonzero'/%3E%3C/svg%3E")}.reward_area{color:rgba(255,255,255,0.8)}.function_hd{color:rgba(255,255,255,0.5)}.reward_whisper_wrp{background-color:rgba(51,51,51,0.9)}.function_mod{background:#232323}}.not_in_mm .rich_media_meta_list{position:relative;z-index:1}.not_in_mm .rich_media_content{position:relative}.not_in_mm .profile_container{width:535px;position:absolute;top:100%;left:0;margin-top:10px;font-size:14px}.not_in_mm .profile_inner{position:relative;padding:30px 22px 36px 144px;background-color:#fff;border:1px solid #d9dadc}.not_in_mm .profile_arrow_wrp{position:absolute;left:22px;top:-8px}.not_in_mm .rich_media_inner{position:relative}.not_in_mm .qr_code_pc_outer{display:none!important;position:fixed;left:0;right:0;top:20px;color:#717375;text-align:center}.not_in_mm .qr_code_pc_inner{position:relative;width:740px;margin-left:auto;margin-right:auto}.not_in_mm .qr_code_pc{position:absolute;right:-140px;top:0;width:140px;padding:16px;border:1px solid #d9dadc;background-color:#fff;word-wrap:break-word;word-break:break-all}.not_in_mm .qr_code_pc p{font-size:14px;line-height:20px}.not_in_mm .qr_code_pc_img{width:102px;height:102px}@media screen and (min-width:1024px){.not_in_mm .qr_code_pc_outer{display:block!important;top:32px}}.not_in_mm .qr_code_pc{box-sizing:border-box}</style><link rel="shortcut icon" type=image/x-icon href=""><style>.sf-hidden{display:none!important}</style><link rel=canonical href=https://mp.weixin.qq.com/s/5Ar5B9Ah2BdO8i9YjMQ7Qg></head>
 <body id=activity-detail class="zh_CN mm_appmsg discuss_tab appmsg_skin_default appmsg_style_default not_in_mm">
 
 
 
 
<div id=js_article class=rich_media>
 
 <div id=js_top_ad_area class=top_banner></div>
 
 <div class=rich_media_inner>
 
 
 <div id=page-content class=rich_media_area_primary>
 <div class=rich_media_area_primary_inner>
 
 
 
 <div id=img-content class=rich_media_wrp>
 
 <h2 class=rich_media_title id=activity-name>
 
 
 
看完这篇，别人的开源项目结构应该能看懂了
 </h2>
 <div id=meta_content class=rich_media_meta_list>
 <span id=copyright_logo class="rich_media_meta icon_appmsg_tag appmsg_title_tag">原创</span>
 <span class="rich_media_meta rich_media_meta_text">
 <span id=js_author_name datarewardsn datatimestamp datacanreward=0 class>hansonwong99</span>
 </span>
 
 <span class="rich_media_meta rich_media_meta_nickname" id=profileBt>
 <a href=https://mp.weixin.qq.com/s/5Ar5B9Ah2BdO8i9YjMQ7Qg id=js_name>
 CodeSheep </a>
 <div id=js_profile_qrcode class=profile_container style=display:none>
 <div class=profile_inner>
 
 
 
 
 
 </div>
 <span class=profile_arrow_wrp id=js_profile_arrow_wrp>
 
 
 </span>
 </div>
 </span>
 <em id=publish_time class="rich_media_meta rich_media_meta_text">4天前</em>
 </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 <div class=rich_media_content id=js_content style=visibility:visible>
 
 
 
 
 
 <section data-tool=mdnice编辑器 data-website=https://www.mdnice.com style='white-space:normal;text-size-adjust:auto;padding-right:10px;padding-left:10px;line-height:1.6;word-break:break-word;overflow-wrap:break-word;text-align:left;font-family:Optima-Regular,Optima,PingFangSC-light,PingFangTC-light,"PingFang SC",Cambria,Cochin,Georgia,Times,"Times New Roman",serif;margin-top:-10px;font-size:15px;letter-spacing:.05em;color:#595959' class><figure data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><img data-ratio=0.42592592592592593 data-src="https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TJwkbexI5aO90IjFc6oOdFEibqSYs7YbmfbJXHJnfgtcicibCItgXDaqqA/640?wx_fmt=png" data-type=png data-w=3240 style=display:block;margin-right:auto;margin-left:auto;width:auto!important;height:auto!important;visibility:visible!important _width=auto src= crossorigin=anonymous data-fail=0 class></figure><hr data-tool=mdnice编辑器 style="height:1px;border-style:solid;border-color:#35b378;margin:1.5em auto"><h2 data-tool=mdnice编辑器 style="line-height:32px;color:#35b378;display:inline-block;border-bottom:0px solid #35b378;border-top-color:#35b378;border-right-color:#35b378;border-left-color:#35b378;font-size:23px;margin-top:1em;margin-bottom:0rem;padding-top:.5em;padding-bottom:.5em;font-weight:bold" class>我为什么要写这篇</h2><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">近来，和不少初学Spring或Spring Boot的小伙伴私信交流了关于项目目录结构划分和代码分层的问题。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">很多小伙伴表示网上下载下来的开源项目看不懂，项目结构和代码分层看得很蒙，不知道应该以一个什么样的思路去学习和吸收别人的项目。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">好，今天熬夜肝了这篇文章，和大家一起来交流探讨一下，不足之处也请小伙伴们批评指正。<hr data-tool=mdnice编辑器 style="height:1px;border-style:solid;border-color:#35b378;margin:1.5em auto"><h2 data-tool=mdnice编辑器 style="line-height:32px;color:#35b378;display:inline-block;border-bottom:0px solid #35b378;border-top-color:#35b378;border-right-color:#35b378;border-left-color:#35b378;font-size:23px;margin-top:1em;margin-bottom:0rem;padding-top:.5em;padding-bottom:.5em;font-weight:bold">先看看阿里是怎么约定的</h2><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">我印象中，以前在看《阿里巴巴Java开发手册》时，好像有关于工程结构和应用分层相关的内容，于是我回翻了一下，果然有：<figure data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><img data-ratio=0.6968036529680365 data-src="https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TnS1Dr6cmia72BxDUKreAH2uFmgTxQ4qpQtDS8t4awnajKxcfAia4licibg/640?wx_fmt=png" data-type=png data-w=2190 style=display:block;margin-right:auto;margin-left:auto;width:auto!important;height:auto!important;visibility:visible!important _width=auto src="" crossorigin=anonymous data-fail=0></figure><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">它这里面讲的内容大概就是：关于一个正常的企业项目里一种<strong style=color:#35b378>通用的项目结构和代码层级划分</strong>的指导意见。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">按这本书上说的，一般分为如下几层：<ul data-tool=mdnice编辑器 style=margin-top:8px;margin-bottom:8px;padding-left:25px;color:black class=list-paddingleft-2><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>开放接口层</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>终端显示层</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>Web 层</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>Service 层</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>Manager 层</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>DAO 层</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px>外部接口或第三方平台</section></ul><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">由于书中的篇幅关系，它这地方讲得比较笼统了，估计初学者看了还是会懵，所以接下来<strong style=color:#35b378>结合实际项目代码结构</strong>，来唠一唠具体的项目结构和代码分层。<hr data-tool=mdnice编辑器 style="height:1px;border-style:solid;border-color:#35b378;margin:1.5em auto"><h2 data-tool=mdnice编辑器 style="line-height:32px;color:#35b378;display:inline-block;border-bottom:0px solid #35b378;border-top-color:#35b378;border-right-color:#35b378;border-left-color:#35b378;font-size:23px;margin-top:1em;margin-bottom:0rem;padding-top:.5em;padding-bottom:.5em;font-weight:bold">通常的项目结构</h2><blockquote data-tool=mdnice编辑器 style="font-size:.9em;overflow:auto;padding:10px 10px 10px 20px;margin:10px 5px;border-left-color:#35b378;border-right:0px solid #35b378;color:#616161;quotes:none;background-color:#fbf9fd"><p style=font-size:16px;padding-top:8px;padding-bottom:8px;color:black;line-height:26px><strong style=color:#35b378>首先说在前面的是</strong>：这东西并没有一套通用的标准，不同公司或者团队的使用习惯和规范也不尽相同。</p></blockquote><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">我们就以当下非常火热的Spring Boot典型项目结构为例，创建出来的项目应该总体分为三大层：<figure data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><img data-ratio=0.4448441247002398 data-src="https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TfbbRJZvR0EicRsasxDPHcALArmBgL6ibVdAUtPoYbIne9sicC1miaCp4QQ/640?wx_fmt=png" data-type=png data-w=1668 style=display:block;margin-right:auto;margin-left:auto;width:auto!important;height:auto!important;visibility:visible!important _width=auto src= crossorigin=anonymous data-fail=0></figure><ul data-tool=mdnice编辑器 style=margin-top:8px;margin-bottom:8px;padding-left:25px;color:black class=list-paddingleft-2><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>项目根目录/src/main/java</code>：放置项目Java源代码</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>项目根目录/src/main/resources</code>：放置项目静态资源和配置文件</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>项目根目录/src/test/java</code>：放置项目测试用例代码</section></ul><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">而位于<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>/src/main/java</code>目录下的Java源代码的组织结构大家比较关心，这地方也只能给出一个通常典型的结构，毕竟不同项目和团队实践不一样，稍许有区别，但整体安排应该差不多。而且如果是<strong style=color:#35b378>多模块</strong>的项目的话，下面的结构应该只对应其中一个模块，其他模块的代码组织也大致差不多。<figure data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><img data-ratio=1.3433476394849786 data-src="https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TwVvJhgMgLG870iajoLYoW9GX5ehGKsat127xM3jYVBGYpRBNkfytANw/640?wx_fmt=png" data-type=png data-w=1398 style=display:block;margin-right:auto;margin-left:auto;width:auto!important;height:auto!important;visibility:visible!important _width=auto src="" crossorigin=anonymous data-fail=0></figure><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">各个目录详细介绍：<pre data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><code style='overflow-x:auto;padding:16px;background-color:#272822;color:#dddddd;display:-webkit-box;font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;font-size:12px'>|_annotation：放置项目自定义注解<br>|_aspect：放置切面代码<br>|_config：放置配置类<br>|_constant：放置常量、枚举等定义<br>   |__consist：存放常量定义<br>   |__enums：存放枚举定义<br>|_controller：放置控制器代码<br>|_filter：放置一些过滤、拦截相关的代码<br>|_mapper：放置数据访问层代码接口<br>|_model：放置数据模型代码<br>   |__entity：放置数据库实体对象定义<br>   |__dto：存放数据传输对象定义<br>   |__vo：存放显示层对象定义<br>|_service：放置具体的业务逻辑代码（接口和实现分离）<br>   |__intf：存放业务逻辑接口定义<br>   |__impl：存放业务逻辑实际实现<br>|_utils：放置工具类和辅助代码<br></code></pre><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">然后接下来<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>/src/main/resources</code>目录，里面主要存放静态配置文件和页面静态资源等东西：<pre data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><code style='overflow-x:auto;padding:16px;background-color:#272822;color:#dddddd;display:-webkit-box;font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;font-size:12px'>|_mapper：存放mybatis的XML映射文件（如果是mybatis项目）<br>|_static：存放网页静态资源，比如下面的js/css/img<br>   |__js：<br>   |__css：<br>   |__img：<br>   |__font：<br>   |__等等<br>|_template：存放网页模板，比如thymeleaf/freemarker模板等<br>   |__header<br>   |__sidebar<br>   |__bottom<br>   |__XXX.html等等<br>|_application.yml       基本配置文件<br>|_application-dev.yml   开发环境配置文件<br>|_application-test.yml  测试环境配置文件<br>|_application-prod.yml  生产环境配置文件<br></code></pre><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">当然，这地方估计有一个<strong style=color:#35b378>很多人都会纠结的</strong>关于<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DTO/VO/DO</code>等<strong style=color:#35b378>数据模型定义</strong>的区分。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">这在《阿里巴巴Java开发手册》中倒是做了一个所谓的严格区分，那本书上是这样去定义的：<ul data-tool=mdnice编辑器 style=margin-top:8px;margin-bottom:8px;padding-left:25px;color:black class=list-paddingleft-2><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DO（Data Object）</code>：与数据库表结构一一对应，通过DAO层向上传输数据源对象。</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DTO（Data Transfer Object）</code>：数据传输对象，Service或Manager向外传输的对象。</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>BO（Business Object）</code>：业务对象。由Service层输出的封装业务逻辑的对象。</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>AO（Application Object）</code>：应用对象。在Web层与Service层之间抽象的复用对象模型，极为贴近展示层，复用度不高。</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>VO（View Object）</code>：显示层对象，通常是Web向模板渲染引擎层传输的对象。</section><li><section style=line-height:26px;color:#010101;margin-top:10px;margin-bottom:10px><code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Query</code>：数据查询对象，各层接收上层的查询请求。注意超过2个参数的查询封装，禁止使用Map类来传输。</section></ul><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">老实讲，看到这么多对象的定义，我也是很蒙的。实际项目开发时，我觉得没有必要刻意照搬去定义这么多层对象，这样后续做对象转换工作都能烦skr人。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">出于简单起见，我个人觉得，只要保证业务逻辑层<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>和数据库<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DAO</code>层的操作对象严格划分出来，确保互相不渗透，不混用，问题应该就不大。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">比如在我上面举例的这个项目的代码结构中，<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>层处理的对象都定义在了<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>dto</code>包里，而<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DAO</code>层处理的对象都放在了<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>entity</code>包里了。<hr data-tool=mdnice编辑器 style="height:1px;border-style:solid;border-color:#35b378;margin:1.5em auto"><h2 data-tool=mdnice编辑器 style="line-height:32px;color:#35b378;display:inline-block;border-bottom:0px solid #35b378;border-top-color:#35b378;border-right-color:#35b378;border-left-color:#35b378;font-size:23px;margin-top:1em;margin-bottom:0rem;padding-top:.5em;padding-bottom:.5em;font-weight:bold">项目结构划分总结</h2><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">如果从一个用户访问一个网站的情况来看，对应着上面的项目代码结构来分析，可以贯穿整个代码分层：<figure data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><img data-ratio=0.9379770992366412 data-src="https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1TTbx21gVqWjqaPBBGk6JzCHQ1kNE9ec5LKGcSnVnYFVqM0npoSpWFrw/640?wx_fmt=png" data-type=png data-w=2096 style=display:block;margin-right:auto;margin-left:auto;width:auto!important;height:auto!important;visibility:visible!important _width=auto src="" crossorigin=anonymous data-fail=0></figure><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px" class>对应代码目录的流转逻辑就是：<figure data-tool=mdnice编辑器 style=margin-top:10px;margin-bottom:10px><img data-ratio=1.332398316970547 data-src="https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzqR0AFjicvvCggzIsm4Nbu1ThYkM0CwRtU2iahqt6NVwzUs9V3lcL8Uia7UR64B0n8tB9HnibbNYRODJQ/640?wx_fmt=png" data-type=png data-w=1426 style=display:block;margin-right:auto;margin-left:auto;width:auto!important;height:auto!important;visibility:visible!important _width=auto src="" crossorigin=anonymous data-fail=0 class></figure><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">我想，应该看得比较清楚了吧。<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px"><strong style=color:#35b378>所以，以后每当我们拿到一个新的项目到手时，只要按照这个思路去看别人项目的代码，应该基本都是能理得顺的</strong>。<hr data-tool=mdnice编辑器 style="height:1px;border-style:solid;border-color:#35b378;margin:1.5em auto"><h2 data-tool=mdnice编辑器 style="line-height:32px;color:#35b378;display:inline-block;border-bottom:0px solid #35b378;border-top-color:#35b378;border-right-color:#35b378;border-left-color:#35b378;font-size:23px;margin-top:1em;margin-bottom:0rem;padding-top:.5em;padding-bottom:.5em;font-weight:bold">一些注意事项</h2><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">1、<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Contorller</code>层参数传递建议不要使用<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>HashMap</code>，建议使用数据模型定义<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">2、<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Controller</code>层里可以做参数校验、异常抛出等操作，但建议不要放太多业务逻辑，业务逻辑尽量放到<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>层代码中去做<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">3、<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>层做实际业务逻辑，可以按照功能模块做好定义和区分，相互可以调用<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px">4、功能模块<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>之间引用时，建议不要渗透到<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DAO</code>层（或者<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>mapper</code>层），基于<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>层进行调用和复用比较合理<p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px" class>5、业务逻辑层<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Service</code>和数据库<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DAO</code>层的操作对象不要混用。<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Controller</code>层的数据对象不要直接渗透到<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>DAO</code>层（或者<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>mapper</code>层）；同理数据表实体对象<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Entity</code>也不要直接传到<code style='font-size:14px;overflow-wrap:break-word;padding:2px 4px;border-radius:4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,0.047);font-family:"Operator Mono",Consolas,Monaco,Menlo,monospace;word-break:break-all;color:#35b378'>Controller</code>层进行输出或展示。<hr data-tool=mdnice编辑器 style="height:1px;border-style:solid;border-color:#35b378;margin:1.5em auto" class><p data-tool=mdnice编辑器 style="font-size:16px;padding-top:8px;padding-bottom:8px;line-height:26px;color:black;margin:1em 4px" class>2020.03.22晚</p></section>
 </div>
 
 
<div id=js_sponsor_ad_area style=display:none></div>
 
 <div class=read-more__area id=js_more_read_area style=display:none>
 
 </div>
 
 
 <div class="reward_area tc reward_area_primary reward_area_carry_whisper" id=js_reward_area style=display:none>
 <div class=reward-avatar style=display:none id=js_reward_avatar>
 
 </div>
 
 <div class=reward-author style=display:none id=js_reward_author>hansonwong99</div>
 <p class=reward_button_wrp>
 
 
 </p>
 
<div id=js_reward_whisper class=reward_whisper_wrp style=display:none>
 
</div>
<div id=js_reward_whisper_dialog style=display:none>
 
 
</div>
 <div id=js_reward_inner class=reward_area_inner style=display:none;width:540px>
 
 
 
 
 </div>
 </div>
 <div class="reward_qrcode_area reward_area tc" id=js_reward_qrcode style=display:none>
 <p class=tips_global>长按二维码向我转账</p>
 <p class=reward_tips></p>
 <span class=reward_qrcode_img_wrp></span>
 <p class=tips_global>受苹果公司新规定影响，微信 iOS 版的赞赏功能被关闭，可通过二维码转账支持公众号。</p>
 </div>
 
 
 </div>
 
 
 
 <ul id=js_hotspot_area class="article_extend_area sf-hidden"></ul>
 
 
<div class=rich_media_tool id=js_toobar3>
 <div class=weui-flex>
 <div class=weui-flex__item>
 
 
 <div id=js_read_area3 class="media_tool_meta tips_global_primary meta_primary" style=display:none>
 <span id=readTxt>阅读</span>
 <span id=readNum3></span>
 </div>
 </div>
 <span style=display:none class="media_tool_meta meta_extra meta_praise" id=like_old>
 <i class=icon_praise_gray></i><span class=praise_num id=likeNum_old></span>
 </span>
 
 <span style=visibility:hidden class="media_tool_meta meta_extra meta_like" id=like3>
 <button class=like_btn id=js_like_btn> 
 <span id=js_like_wording>在看</span><span class=like_num id=likeNum3></span>
 </button>
</span>
 
 </div> 
</div>
 
 <div class=like_comment_wrp id=js_like_comment style=display:none>
 <div class=like_comment_inner>
 
 
 </div>
 </div>
 <div style=display:none id=wow_close_inform>
 <div class=weui-mask></div>
 <div class=weui-dialog>
 
 
 
 </div>
 </div>
<div id=js_like_toast style=display:none>
 <div class=weui-mask_transparent></div>
 <div class=weui-toast>
 
 
 </div>
</div>
<div style=display:none id=js_comment_panel>
 <div class="like_comment_primary_wrp editing" id=js_comment_wrp>
 
 </div>
 <div class=like_comment_primary_mask id=js_mask_2></div>
</div>
<div id=js_loading style=display:none>
 <div class=weui-mask_transparent></div>
 <div class=weui-toast>
 
 
 </div>
</div>
 </div>
 </div>
 <div class="rich_media_area_primary sougou" id=sg_tj style=display:none></div>
 
 <div class=rich_media_area_extra>
 <div class=rich_media_area_extra_inner>
 
 <div id=js_share_appmsg>
 </div>
 
<div class=mpda_bottom_container id=js_bottom_ad_area style=display:none></div>
 
 <div id=js_iframetest style=display:none></div>
 
 <div class="rich_media_extra rich_media_extra_discuss" id=js_cmt_container style=display:none>
 
 
 <div class=discuss_mod id=js_friend_cmt_area style=display:none>
 
 
 
 </div>
 <div class=discuss_mod id=js_cmt_area style=display:none>
 </div>
 </div>
 <div class="function_mod function_mod_index" id=js_related_area style=display:none>
 <div class=function_hd>相关阅读</div>
 <div class=function_bd>
 
 
 
 
 
 
 </div>
 </div>
 </div>
 </div>
 
 <div id=js_pc_qr_code class=qr_code_pc_outer style=display:block>
 <div class=qr_code_pc_inner>
 
 </div>
 </div>
 </div>
</div>
<div id=js_pc_weapp_code class="weui-desktop-popover weui-desktop-popover_pos-up-center weui-desktop-popover_img-text" style=display:none>
 <div class=weui-desktop-popover__content>
 
 </div>
</div>
<div id=js_minipro_dialog style=display:none>
 <div class=weui-mask></div>
 <div class="weui-dialog weui-dialog_link">
 
 
 
 
 </div>
</div>
<div id=js_link_dialog style=display:none>
 <div class=weui-mask></div>
 <div class="weui-dialog weui-dialog_link">
 
 
 
 
 </div>
</div>
<div class=comment_primary_emotion_panel_wrp id=js_emotion_panel_pc style=display:none>
 <div class=comment_primary_emotion_panel>
 
 </div>
</div>
<div class=weui-dialog__wrp id=js_alert_panel style=display:none>
 <div class=weui-mask></div>
 <div class=weui-dialog>
 
 
 </div>
</div>
<div id=js_weapp_without_auth_dialog style=display:none>
 <div id=js_weapp_without_auth_dialog_mask class=weui-mask></div>
 <div class="weui-dialog weui-dialog_link">
 
 
 </div>
</div>
 
 
 
 
 
 
<div style=display:none>
 <div class=weui-mask_transparent></div>
 <div class=weui-toast>
 
 
 </div>
</div><script data-template-shadow-root>(() => { document.currentScript.remove(); const processNode = node => { node.querySelectorAll("template[shadowmode]").forEach(element=>{ if (!element.parentElement.shadowRoot) { const shadowRoot = element.parentElement.attachShadow({mode:element.getAttribute("shadowmode"),delegatesFocus:Boolean(element.getAttribute("delegatesfocus"))}); shadowRoot.innerHTML = element.innerHTML; element.remove(); processNode(shadowRoot); } }) }; const FORBIDDEN_TAG_NAMES = ["a","area","audio","base","br","col","command","embed","hr","img","iframe","input","keygen","link","meta","param","source","track","video","wbr"]; const NOTE_TAGNAME = "single-file-note"; const NOTE_CLASS = "note"; const NOTE_ANCHORED_CLASS = "note-anchored"; const NOTE_SELECTED_CLASS = "note-selected"; const NOTE_MOVING_CLASS = "note-moving"; const NOTE_MASK_MOVING_CLASS = "note-mask-moving"; const MASK_CLASS = "single-file-mask"; const HIGHLIGHT_CLASS = "single-file-highlight"; const NOTES_WEB_STYLESHEET = ".note { all: initial; display: flex; flex-direction: column; height: 150px; width: 150px; position: absolute; top: 10px; left: 10px; border: 1px solid rgb(191, 191, 191); z-index: 2147483646; box-shadow: 2px 2px 5px rgba(33, 33, 33, .7); min-height: 100px; min-width: 100px; } .note-selected { z-index: 2147483647; } .note-hidden { display: none; } .note-collapsed { min-height: 30px; max-height: 30px; overflow: hidden; } .note textarea { all: initial; white-space: pre; font-family: Arial, Helvetica, sans-serif; font-size: 14px; padding: 3px; height: 100%; border: 1px solid transparent; resize: none; } .note textarea:focus { border: 1px dotted rgb(160, 160, 160); } .note header { all: initial; min-height: 30px; cursor: grab; user-select: none; } .note .note-remove { all: initial; position: absolute; right: 0px; top: 2px; padding: 5px; opacity: .5; cursor: pointer; user-select: none; } .note .note-anchor { all: initial; position: absolute; left: 0px; top: 2px; padding: 5px; opacity: .25; cursor: pointer; } .note .note-resize { all: initial; position: absolute; bottom: -5px; right: -5px; height: 15px; width: 15px; cursor: nwse-resize; user-select: none; } .note .note-remove:hover { opacity: 1; } .note .note-anchor:hover { opacity: .5; } .note-anchored .note-anchor { opacity: .5; } .note-anchored .note-anchor:hover { opacity: 1; } .note-moving { opacity: .5; } .note-moving * { cursor: grabbing; } .note-yellow header { background-color: #f5f545; } .note-yellow textarea { background-color: #ffff7c; } .note-pink header { background-color: #ffa59f; } .note-pink textarea { background-color: #ffbbb6; } .note-blue header { background-color: #84c8ff; } .note-blue textarea { background-color: #95d0ff; } .note-green header { background-color: #93ef8d; } .note-green textarea { background-color: #9cff95; }"; const MASK_WEB_STYLESHEET = ".note-mask { all: initial; position: fixed; z-index: 2147483645; pointer-events: none; } .note-mask-moving { background-color: rgba(236, 234, 234, 0.5); } .page-mask { all: initial; position: fixed; top: 0; left: 0; width: 0; height: 0; z-index: 2147483646; } .page-mask-active { width: 100vw; height: 100vh; }"; const NOTE_HEADER_HEIGHT = 25; const PAGE_MASK_ACTIVE_CLASS = "page-mask-active"; const REMOVED_CONTENT_CLASS = "single-file-removed"; const reflowNotes = function reflowNotes() { document.querySelectorAll(NOTE_TAGNAME).forEach(containerElement => { const noteElement = containerElement.shadowRoot.querySelector("." + NOTE_CLASS); const noteBoundingRect = noteElement.getBoundingClientRect(); const anchorElement = getAnchorElement(containerElement); const anchorBoundingRect = anchorElement.getBoundingClientRect(); const maxX = anchorBoundingRect.x + Math.max(0, anchorBoundingRect.width - noteBoundingRect.width); const minX = anchorBoundingRect.x; const maxY = anchorBoundingRect.y + Math.max(0, anchorBoundingRect.height - NOTE_HEADER_HEIGHT); const minY = anchorBoundingRect.y; let left = parseInt(noteElement.style.getPropertyValue("left")); let top = parseInt(noteElement.style.getPropertyValue("top")); if (noteBoundingRect.x > maxX) { left -= noteBoundingRect.x - maxX; } if (noteBoundingRect.x < minX) { left += minX - noteBoundingRect.x; } if (noteBoundingRect.y > maxY) { top -= noteBoundingRect.y - maxY; } if (noteBoundingRect.y < minY) { top += minY - noteBoundingRect.y; } noteElement.style.setProperty("position", "absolute"); noteElement.style.setProperty("left", left + "px"); noteElement.style.setProperty("top", top + "px"); }); }; const addNoteRef = function addNoteRef(anchorElement, noteId) { const noteRefs = getNoteRefs(anchorElement); noteRefs.push(noteId); setNoteRefs(anchorElement, noteRefs); }; const deleteNoteRef = function deleteNoteRef(containerElement, noteId) { const anchorElement = getAnchorElement(containerElement); const noteRefs = getNoteRefs(anchorElement).filter(noteRefs => noteRefs != noteId); if (noteRefs.length) { setNoteRefs(anchorElement, noteRefs); } else { delete anchorElement.dataset.singleFileNoteRefs; } }; const getNoteRefs = function getNoteRefs(anchorElement) { return JSON.parse("[" + (anchorElement.dataset.singleFileNoteRefs || "") + "]"); }; const setNoteRefs = function setNoteRefs(anchorElement, noteRefs) { anchorElement.dataset.singleFileNoteRefs = noteRefs.toString(); }; const getAnchorElement = function getAnchorElement(containerElement) { return document.querySelector("[data-single-file-note-refs^=" + JSON.stringify(containerElement.dataset.noteId) + "], [data-single-file-note-refs$=" + JSON.stringify(containerElement.dataset.noteId) + "], [data-single-file-note-refs*=" + JSON.stringify("," + containerElement.dataset.noteId + ",") + "]"); }; const getMaskElement = function getMaskElement(className, containerClassName) { let maskElement = document.documentElement.querySelector("." + className); if (!maskElement) { maskElement = document.createElement("div"); const maskContainerElement = document.createElement("div"); if (containerClassName) { maskContainerElement.classList.add(containerClassName); } maskContainerElement.classList.add(MASK_CLASS); const firstNote = document.querySelector(NOTE_TAGNAME); if (firstNote && firstNote.parentElement == document.documentElement) { document.documentElement.insertBefore(maskContainerElement, firstNote); } else { document.documentElement.appendChild(maskContainerElement); } maskElement.classList.add(className); const maskShadow = maskContainerElement.attachShadow({ mode: "open" }); maskShadow.appendChild(getStyleElement(MASK_WEB_STYLESHEET)); maskShadow.appendChild(maskElement); return maskElement; } }; const getStyleElement = function getStyleElement(stylesheet) { const linkElement = document.createElement("style"); linkElement.textContent = stylesheet; return linkElement; }; const attachNoteListeners = function attachNoteListeners(containerElement, editable = false) { const SELECT_PX_THRESHOLD = 4; const COLLAPSING_NOTE_DELAY = 750; const noteShadow = containerElement.shadowRoot; const noteElement = noteShadow.childNodes[1]; const headerElement = noteShadow.querySelector("header"); const mainElement = noteShadow.querySelector("textarea"); const noteId = containerElement.dataset.noteId; const resizeElement = noteShadow.querySelector(".note-resize"); const anchorIconElement = noteShadow.querySelector(".note-anchor"); const removeNoteElement = noteShadow.querySelector(".note-remove"); mainElement.readOnly = !editable; if (!editable) { anchorIconElement.style.setProperty("display", "none", "important"); } else { anchorIconElement.style.removeProperty("display"); } headerElement.ontouchstart = headerElement.onmousedown = event => { if (event.target == headerElement) { collapseNoteTimeout = setTimeout(() => noteElement.classList.toggle("note-collapsed"), COLLAPSING_NOTE_DELAY); event.preventDefault(); const position = getPosition(event); const clientX = position.clientX; const clientY = position.clientY; const boundingRect = noteElement.getBoundingClientRect(); const deltaX = clientX - boundingRect.left; const deltaY = clientY - boundingRect.top; maskPageElement.classList.add(PAGE_MASK_ACTIVE_CLASS); document.documentElement.style.setProperty("user-select", "none", "important"); anchorElement = getAnchorElement(containerElement); displayMaskNote(); selectNote(noteElement); moveNote(event, deltaX, deltaY); movingNoteMode = { event, deltaX, deltaY }; document.documentElement.ontouchmove = document.documentElement.onmousemove = event => { clearTimeout(collapseNoteTimeout); if (!movingNoteMode) { movingNoteMode = { deltaX, deltaY }; } movingNoteMode.event = event; moveNote(event, deltaX, deltaY); }; } }; resizeElement.ontouchstart = resizeElement.onmousedown = event => { event.preventDefault(); resizingNoteMode = true; selectNote(noteElement); maskPageElement.classList.add(PAGE_MASK_ACTIVE_CLASS); document.documentElement.style.setProperty("user-select", "none", "important"); document.documentElement.ontouchmove = document.documentElement.onmousemove = event => { event.preventDefault(); const { clientX, clientY } = getPosition(event); const boundingRectNote = noteElement.getBoundingClientRect(); noteElement.style.width = clientX - boundingRectNote.left + "px"; noteElement.style.height = clientY - boundingRectNote.top + "px"; }; }; anchorIconElement.ontouchend = anchorIconElement.onclick = event => { event.preventDefault(); noteElement.classList.toggle(NOTE_ANCHORED_CLASS); if (!noteElement.classList.contains(NOTE_ANCHORED_CLASS)) { deleteNoteRef(containerElement, noteId); addNoteRef(document.documentElement, noteId); } }; removeNoteElement.ontouchend = removeNoteElement.onclick = event => { event.preventDefault(); deleteNoteRef(containerElement, noteId); containerElement.remove(); }; noteElement.onmousedown = () => { selectNote(noteElement); }; function moveNote(event, deltaX, deltaY) { event.preventDefault(); const { clientX, clientY } = getPosition(event); noteElement.classList.add(NOTE_MOVING_CLASS); if (editable) { if (noteElement.classList.contains(NOTE_ANCHORED_CLASS)) { deleteNoteRef(containerElement, noteId); anchorElement = getTarget(clientX, clientY) || document.documentElement; addNoteRef(anchorElement, noteId); } else { anchorElement = document.documentElement; } } document.documentElement.insertBefore(containerElement, maskPageElement.getRootNode().host); noteElement.style.setProperty("left", (clientX - deltaX) + "px"); noteElement.style.setProperty("top", (clientY - deltaY) + "px"); noteElement.style.setProperty("position", "fixed"); displayMaskNote(); } function displayMaskNote() { if (anchorElement == document.documentElement || anchorElement == document.documentElement) { maskNoteElement.classList.remove(NOTE_MASK_MOVING_CLASS); } else { const boundingRectAnchor = anchorElement.getBoundingClientRect(); maskNoteElement.classList.add(NOTE_MASK_MOVING_CLASS); maskNoteElement.style.setProperty("top", boundingRectAnchor.y + "px"); maskNoteElement.style.setProperty("left", boundingRectAnchor.x + "px"); maskNoteElement.style.setProperty("width", boundingRectAnchor.width + "px"); maskNoteElement.style.setProperty("height", boundingRectAnchor.height + "px"); } } function selectNote(noteElement) { if (selectedNote) { selectedNote.classList.remove(NOTE_SELECTED_CLASS); } noteElement.classList.add(NOTE_SELECTED_CLASS); selectedNote = noteElement; } function getTarget(clientX, clientY) { const targets = Array.from(document.elementsFromPoint(clientX, clientY)).filter(element => element.tagName.toLowerCase() != NOTE_TAGNAME && !element.classList.contains(MASK_CLASS)); if (!targets.includes(document.documentElement)) { targets.push(document.documentElement); } let newTarget, target = targets[0], boundingRect = target.getBoundingClientRect(); newTarget = determineTargetElement("floor", target, clientX - boundingRect.left, getMatchedParents(target, "left")); if (newTarget == target) { newTarget = determineTargetElement("ceil", target, boundingRect.left + boundingRect.width - clientX, getMatchedParents(target, "right")); } if (newTarget == target) { newTarget = determineTargetElement("floor", target, clientY - boundingRect.top, getMatchedParents(target, "top")); } if (newTarget == target) { newTarget = determineTargetElement("ceil", target, boundingRect.top + boundingRect.height - clientY, getMatchedParents(target, "bottom")); } target = newTarget; while (boundingRect = target && target.getBoundingClientRect(), boundingRect && boundingRect.width <= SELECT_PX_THRESHOLD && boundingRect.height <= SELECT_PX_THRESHOLD) { target = target.parentElement; } return target; } function getMatchedParents(target, property) { let element = target, matchedParent, parents = []; do { const boundingRect = element.getBoundingClientRect(); if (element.parentElement && !element.parentElement.tagName.toLowerCase() != NOTE_TAGNAME && !element.classList.contains(MASK_CLASS)) { const parentBoundingRect = element.parentElement.getBoundingClientRect(); matchedParent = Math.abs(parentBoundingRect[property] - boundingRect[property]) <= SELECT_PX_THRESHOLD; if (matchedParent) { if (element.parentElement.clientWidth > SELECT_PX_THRESHOLD && element.parentElement.clientHeight > SELECT_PX_THRESHOLD && ((element.parentElement.clientWidth - element.clientWidth > SELECT_PX_THRESHOLD) || (element.parentElement.clientHeight - element.clientHeight > SELECT_PX_THRESHOLD))) { parents.push(element.parentElement); } element = element.parentElement; } } else { matchedParent = false; } } while (matchedParent && element); return parents; } function determineTargetElement(roundingMethod, target, widthDistance, parents) { if (Math[roundingMethod](widthDistance / SELECT_PX_THRESHOLD) <= parents.length) { target = parents[parents.length - Math[roundingMethod](widthDistance / SELECT_PX_THRESHOLD) - 1]; } return target; } }; const anchorNote = function anchorNote(event, noteElement, deltaX, deltaY) { event.preventDefault(); const { clientX, clientY } = getPosition(event); document.documentElement.style.removeProperty("user-select"); noteElement.classList.remove(NOTE_MOVING_CLASS); maskNoteElement.classList.remove(NOTE_MASK_MOVING_CLASS); maskPageElement.classList.remove(PAGE_MASK_ACTIVE_CLASS); const headerElement = noteElement.querySelector("header"); headerElement.ontouchmove = document.documentElement.onmousemove = null; let currentElement = anchorElement; let positionedElement; while (currentElement.parentElement && !positionedElement) { if (!FORBIDDEN_TAG_NAMES.includes(currentElement.tagName.toLowerCase())) { const currentElementStyle = getComputedStyle(currentElement); if (currentElementStyle.position != "static") { positionedElement = currentElement; } } currentElement = currentElement.parentElement; } if (!positionedElement) { positionedElement = document.documentElement; } const containerElement = noteElement.getRootNode().host; if (positionedElement == document.documentElement) { const firstMaskElement = document.querySelector("." + MASK_CLASS); document.documentElement.insertBefore(containerElement, firstMaskElement); } else { positionedElement.appendChild(containerElement); } const boundingRectPositionedElement = positionedElement.getBoundingClientRect(); const stylePositionedElement = window.getComputedStyle(positionedElement); const borderX = parseInt(stylePositionedElement.getPropertyValue("border-left-width")); const borderY = parseInt(stylePositionedElement.getPropertyValue("border-top-width")); noteElement.style.setProperty("position", "absolute"); noteElement.style.setProperty("left", (clientX - boundingRectPositionedElement.x - deltaX - borderX) + "px"); noteElement.style.setProperty("top", (clientY - boundingRectPositionedElement.y - deltaY - borderY) + "px"); }; const getPosition = function getPosition(event) { if (event.touches && event.touches.length) { const touch = event.touches[0]; return touch; } else { return event; } }; const onMouseUp = function onMouseUp(event) { if (highlightSelectionMode) { highlightSelection(); } if (removeHighlightMode) { let element = event.target, done; while (element && !done) { if (element.classList.contains(HIGHLIGHT_CLASS)) { document.querySelectorAll("." + HIGHLIGHT_CLASS + "[data-singlefile-highlight-id=" + JSON.stringify(element.dataset.singlefileHighlightId) + "]").forEach(highlightedElement => { resetHighlightedElement(highlightedElement); }); done = true; } element = element.parentElement; } } if (resizingNoteMode) { resizingNoteMode = false; document.documentElement.style.removeProperty("user-select"); maskPageElement.classList.remove(PAGE_MASK_ACTIVE_CLASS); document.documentElement.ontouchmove = document.documentElement.onmousemove = null; } if (movingNoteMode) { anchorNote(movingNoteMode.event || event, selectedNote, movingNoteMode.deltaX, movingNoteMode.deltaY); movingNoteMode = null; document.documentElement.ontouchmove = document.documentElement.onmousemove = null; } if (collapseNoteTimeout) { clearTimeout(collapseNoteTimeout); collapseNoteTimeout = null; } if (cuttingMode) { let element = event.target; if (document.documentElement != element && element.tagName.toLowerCase() != NOTE_TAGNAME) { element.classList.add(REMOVED_CONTENT_CLASS); removedElements.push(element); } } }; const maskNoteElement = getMaskElement("note-mask"); const maskPageElement = getMaskElement("page-mask", "single-file-page-mask"); let selectedNote, highlightSelectionMode, removeHighlightMode, resizingNoteMode, movingNoteMode, collapseNoteTimeout, cuttingMode; window.onresize = reflowNotes; document.documentElement.onmouseup = document.documentElement.ontouchend = onMouseUp; window.addEventListener("DOMContentLoaded", () => { processNode(document); reflowNotes(); document.querySelectorAll("single-file-note").forEach(noteElement => attachNoteListeners(noteElement)); }); })()</script><style>.single-file-highlight-yellow { background-color: #ffff7c !important; color: black !important; } span.single-file-highlight-yellow { display: inline !important; } .single-file-highlight-pink { background-color: #ffbbb6 !important; color: black !important; } span.single-file-highlight-pink { display: inline !important; } .single-file-highlight-blue { background-color: #95d0ff !important; color: black !important; } span.single-file-highlight-blue { display: inline !important; } .single-file-highlight-green { background-color: #93ef8d !important; color: black !important; } span.single-file-highlight-green { display: inline !important; } .single-file-highlight-hidden { background-color: inherit !important; color: inherit !important; } .single-file-mask { all: initial; display: contents !important; } .single-file-mask.single-file-page-mask { opacity: .99 !important; } single-file-note { all: initial !important; display: contents !important; } .single-file-hover { outline: 1px solid red !important; } .single-file-removed { display: none !important; float: none !important; position: static !important; visibility: collapse !important; }</style>